#include "string.h"
#include "mailbox.h"

#define MAX_NUM_BOX 32

mailbox_t mboxs[MAX_NUM_BOX];
mutex_lock_t alloc_lock;

static int do_mbox_is_full(mailbox_t *mailbox, int msg_length)
{
  if (mailbox->valid_size < msg_length)
    return 1;
  else
    return 0;
}

static int do_mbox_is_empty(mailbox_t *mailbox, int msg_length)
{
  if (mailbox->valid_size + msg_length > MAX_MBOX_LENGTH)
    return 1;
  else
    return 0;
}

static void do_init_mbox(mailbox_t *mailbox)
{
  mailbox->num = 0;
  bzero(&mailbox->name, sizeof(mailbox->name));
  mailbox->send_ptr = 0;
  mailbox->recv_ptr = 0;
  mailbox->valid_size = MAX_MBOX_LENGTH;
  queue_init(&(mailbox->waiting_queue));
}

void do_mbox_init()
{
  int i = 0;
  for (i = 0; i < MAX_NUM_BOX; i++)
  {
    mboxs[i].num = 0;
    bzero(&(mboxs[i].name), sizeof(mboxs[i].name));
    mboxs[i].send_ptr = 0;
    mboxs[i].recv_ptr = 0;
    mboxs[i].valid_size = MAX_MBOX_LENGTH;
    bzero(&mboxs[i].msg, sizeof(mboxs[i].msg));
    queue_init(&(mboxs[i].waiting_queue));
  }
}

mailbox_t *do_mbox_open(char *name)
{
  int i = 0;
  for (i = 0; i < MAX_NUM_BOX; i++)
  {
    // name matched and mailbox exists
    if (strcmp(name, mboxs[i].name) == 0 && mboxs[i].num > 0)
    {
      mboxs[i].num++;
      return &(mboxs[i]);
    }
  }

  for (i = 0; i < MAX_NUM_BOX; i++)
  {
    if (mboxs[i].num == 0)
    {
      do_init_mbox(&(mboxs[i]));
      strcpy(mboxs[i].name, name);
      mboxs[i].num++;
      return &(mboxs[i]);
    }
  }
}

void do_mbox_close(mailbox_t *mailbox)
{
  if (mailbox->num == 0)
  {
    do_init_mbox(mailbox);
  }
  else
  {
    mailbox->num--;
  }
}

void do_mbox_send(mailbox_t *mailbox, void *msg, int msg_length)
{
  while (do_mbox_is_full(mailbox, msg_length))
  {
    current_running[core_id]->status = TASK_BLOCKED;
    queue_push(&(mailbox->waiting_queue), current_running[core_id]);
    do_scheduler();
  }
  while (!queue_is_empty(&(mailbox->waiting_queue)))
  {
    pcb_t *first_task = queue_dequeue(&(mailbox->waiting_queue));
    first_task->status = TASK_READY;
    queue_push(&ready_queue, first_task);
  }
  memcpy((uint8_t *)(&(mailbox->msg[mailbox->send_ptr])), msg, msg_length);
  mailbox->send_ptr = (mailbox->send_ptr + msg_length) % MAX_MBOX_LENGTH;
  mailbox->valid_size -= msg_length;
}

void do_mbox_recv(mailbox_t *mailbox, void *msg, int msg_length)
{
  while (do_mbox_is_empty(mailbox, msg_length))
  {
    current_running[core_id]->status = TASK_BLOCKED;
    queue_push(&(mailbox->waiting_queue), current_running[core_id]);
    do_scheduler();
  }
  while (!queue_is_empty(&(mailbox->waiting_queue)))
  {
    pcb_t *first_task = queue_dequeue(&(mailbox->waiting_queue));
    first_task->status = TASK_READY;
    queue_push(&ready_queue, first_task);
  }
  memcpy(msg, (uint8_t *)(&(mailbox->msg[mailbox->recv_ptr])), msg_length);
  mailbox->recv_ptr = (mailbox->recv_ptr + msg_length) % MAX_MBOX_LENGTH;
  mailbox->valid_size += msg_length;
}
